package com.hipac.hq;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.impl.source.PsiJavaFileImpl;

import java.util.ArrayList;
import java.util.List;

/**
 * 自动修正test方法名称
 *
 * @author huqiang
 */
public class AutoCompletionTest extends AnAction {
    private static final Logger LOG = Logger.getInstance(AutoCompletionTest.class);

    @Override
    public void actionPerformed(AnActionEvent anActionEvent) {
        PsiElement psiElement = getPsiElement(anActionEvent);
        bean2JsonMethod(psiElement);
    }


    /**
     * 启动写线程
     *
     * @param psiElement
     */
    private void bean2JsonMethod(final PsiElement psiElement) {
        new WriteCommandAction.Simple(psiElement.getProject(), psiElement.getContainingFile()) {
            @Override
            protected void run() throws Throwable {
                modifyTestMethod(psiElement);
            }
        }.execute();
    }

    private void modifyTestMethod(PsiElement psiElement) {
        Project project = psiElement.getProject();
        VirtualFile[] selectedFiles = FileEditorManager.getInstance(project).getSelectedFiles();
        final Module module = ModuleUtil.findModuleForFile(
                selectedFiles[0], project);
        System.out.println(module);
        VirtualFile[] sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots(true);
        List<VirtualFile> virtualFiles = flattenFiles(sourceRoots);

        List<PsiFile> fileList = findAllFilesFor(virtualFiles, project);

        for (PsiFile psiFile : fileList) {
            if (StdFileTypes.JAVA.equals(psiFile.getFileType())){
                modifyTestMethodName(psiFile);
            }
        }
    }

    private List<PsiFile> findAllFilesFor(final List<VirtualFile> virtualFiles, Project project) {
        final List<PsiFile> childFiles = new ArrayList<>();
        final PsiManager psiManager = PsiManager.getInstance(project);
        for (final VirtualFile virtualFile : virtualFiles) {
            childFiles.addAll(buildFilesList(psiManager, virtualFile));
        }
        return childFiles;
    }


    private List<PsiFile> buildFilesList(final PsiManager psiManager, final VirtualFile virtualFile) {
        final List<PsiFile> allChildFiles = new ArrayList<>();
        ApplicationManager.getApplication().runReadAction(() -> {
            final FindChildFiles visitor = new FindChildFiles(virtualFile, psiManager);
            VfsUtilCore.visitChildrenRecursively(virtualFile, visitor);
            allChildFiles.addAll(visitor.locatedFiles);
        });
        return allChildFiles;
    }

    private void modifyTestMethodName(PsiElement psiElement) {
        PsiJavaFileImpl javaFile = null;
        if (psiElement instanceof PsiJavaFileImpl) {
            javaFile = (PsiJavaFileImpl) psiElement;
        }
        if (javaFile == null) {
            return ;
        }

        PsiClass javaFileClass = javaFile.getClasses()[0];
        PsiMethod[] methods = javaFileClass.getMethods();
        for (PsiMethod method : methods) {
            boolean hasTest = method.hasAnnotation("org.junit.Test");
            if (!hasTest){
                continue;
            }
            String name = method.getName();
            if (name.startsWith("test")){
                continue;
            }
            method.setName("test"+toUpperFirstChar(method.getName()));
        }
        PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(psiElement.getProject());

    }


    private List<VirtualFile> flattenFiles(final VirtualFile[] files) {
        final List<VirtualFile> flattened = new ArrayList<>();

        if (files != null) {
            for (final VirtualFile file : files) {
                flattened.add(file);

                if (file.getChildren() != null) {
                    flattened.addAll(flattenFiles(file.getChildren()));
                }
            }
        }

        return flattened;
    }

    private PsiElement getPsiElement(AnActionEvent e) {
        PsiFile psiFile = e.getData(LangDataKeys.PSI_FILE);
        Editor editor = e.getData(PlatformDataKeys.EDITOR);
        if (psiFile == null || editor == null || !(psiFile instanceof PsiJavaFileImpl)) {
            e.getPresentation().setEnabled(false);
            return null;
        }

        return psiFile.getNavigationElement();
    }

    public static String toUpperFirstChar(String string) {
        char[] charArray = string.toCharArray();
        charArray[0] -= 32;
        return String.valueOf(charArray);
    }


    private class FindChildFiles extends VirtualFileVisitor {

        private final VirtualFile virtualFile;
        private final PsiManager psiManager;

        private final List<PsiFile> locatedFiles = new ArrayList<>();

        FindChildFiles(final VirtualFile virtualFile, final PsiManager psiManager) {
            this.virtualFile = virtualFile;
            this.psiManager = psiManager;
        }

        @Override
        public boolean visitFile( final VirtualFile file) {
            if (!file.isDirectory()) {
                final PsiFile psiFile = psiManager.findFile(virtualFile);
                if (psiFile != null) {
                    locatedFiles.add(psiFile);
                }
            }
            return true;
        }
    }
}
